using System;
using System.Collections.Generic;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Text;
using System.Data.SqlClient;
using AUSRIS.Framework.Logging;

namespace AUSRIS.Reports.Extensions.Security.Authorization
{
	/// <summary>
	/// DNN based implementation of ISecurityProvider.
	/// Since the roles in DNN are portal-specific, a 
	/// global list of roles required by SSRS is made 
	/// by concatenating the portal ID with the name 
	/// of the role, separated by a dot.
	/// i.e. "1.Student".
	/// </summary>
	public class DNNSecurityProvider : ISecurityProvider
	{
		#region Private Fields

		private string connectionString;
        private string dnnObjectQualifier;
		private static readonly string everyoneRoleName = "Everyone";

		#endregion

		#region Public Properties

		/// <summary>
		/// Connection string to the DNN database
		/// (Set via Dependency Injection)
		/// </summary>
		public string ConnectionString
		{
			get
			{
				return this.connectionString;
			}
			set
			{
				this.connectionString = value;
			}
		}

        /// <summary>
        /// DNN Object Qualifier
        /// (Set via Dependency Injection)
        /// </summary>
        public string DnnObjectQualifier 
        {
            get 
            { 
                return this.dnnObjectQualifier; 
            }
            set
            {
                this.dnnObjectQualifier = value;
            }
        }

		#endregion

		#region ISecurityProvider Members

		/// <summary>
		/// Returns true if the principal name is
		/// an existing role or username.
		/// </summary>
		/// <param name="principalName"></param>
		/// <returns></returns>
		public bool IsValidPrincipalName(string principalName)
		{
#if DEBUG
			Log.Debug(Log.GetSource(), "principalName: {0}", principalName);
#endif
			if ( string.Compare(principalName, everyoneRoleName, true) == 0 )
			{
				return true;
			}
			else
			{
				bool isValid = this.RoleExists(principalName);
				if ( !isValid )
				{
					isValid = this.UsernameExists(principalName);
				}
				return isValid;
			}
		}

		/// <summary>
		/// Returns true if the credentials suppied are valid according the the specified authority
		/// </summary>
		/// <param name="userName"></param>
		/// <param name="password"></param>
		/// <param name="authority"></param>
		/// <returns></returns>
		/// <remarks>
		/// If the authority is blank, assumes the default authority, which is the DNN Username/password store
		/// </remarks>
		public bool IsValidCredentials(string userName, string password, string authority)
		{
#if DEBUG
			Log.Debug(Log.GetSource(), "userName: {0}", userName);
#endif
			if ( string.Compare(authority, "ClientCertificate", true) == 0 )
			{
				return this.UsernameExists(userName);
			}
			else
			{
				return this.IsValidCredentials(userName, password);
			}
		}

		/// <summary>
		/// List the roles currently assigned to the user.
		/// </summary>
		/// <param name="userName"></param>
		/// <returns></returns>
		public string[] ListRoles(string userName)
		{
#if DEBUG
			Log.Debug(Log.GetSource(), "userName: {0}", userName);
#endif
			List<string> roles = new List<string>();
			StringBuilder sql = new StringBuilder();
            sql.AppendFormat("SELECT ");
            sql.AppendFormat("	CAST(dbo.{0}Roles.PortalId AS VARCHAR(20)) + '.' + dbo.{0}Roles.RoleName AS RoleName ", this.DnnObjectQualifier);
            sql.AppendFormat("FROM  ");
            sql.AppendFormat("	dbo.{0}UserRoles ", this.DnnObjectQualifier);
            sql.AppendFormat("	INNER JOIN dbo.{0}Users ON dbo.{0}UserRoles.UserId = dbo.{0}Users.UserId ", this.DnnObjectQualifier);
            sql.AppendFormat("	INNER JOIN dbo.{0}Roles ON dbo.{0}UserRoles.RoleId = dbo.{0}Roles.RoleId ", this.DnnObjectQualifier);
            sql.AppendFormat("WHERE ");
            sql.AppendFormat("	dbo.{0}Users.Username = @Username ", this.DnnObjectQualifier);
            sql.AppendFormat("	AND (dbo.{0}UserRoles.EffectiveDate <= GETDATE() OR dbo.{0}UserRoles.EffectiveDate IS NULL) ", this.DnnObjectQualifier);
            sql.AppendFormat("	AND (dbo.{0}UserRoles.ExpiryDate >= GETDATE() OR dbo.{0}UserRoles.ExpiryDate IS NULL);", this.DnnObjectQualifier);
			string commandText = sql.ToString();
			using ( SqlConnection connection = new SqlConnection(connectionString) )
			{
				SqlCommand command = new SqlCommand(commandText, connection);
				command.Parameters.AddWithValue("@Username", userName);
				connection.Open();
				using ( SqlDataReader reader = command.ExecuteReader() )
				{
					while ( reader.Read() )
					{
						roles.Add(reader.GetString(reader.GetOrdinal("RoleName")));
					}
					reader.Close();
				}
				connection.Close();
			}
			roles.Add(everyoneRoleName);
			return roles.ToArray();
		}

		/// <summary>
		/// Lists the usernames of the host accounts
		/// </summary>
		public string[] ListAdministrators()
		{
			List<string> userNames = new List<string>();
			StringBuilder sql = new StringBuilder();
			sql.AppendFormat("SELECT ");
            sql.AppendFormat("	dbo.{0}Users.UserName ", this.DnnObjectQualifier);
            sql.AppendFormat("FROM  ");
            sql.AppendFormat("	dbo.{0}Users ", this.DnnObjectQualifier);
            sql.AppendFormat("WHERE ");
            sql.AppendFormat("	dbo.{0}Users.IsSuperUser = 1;", this.DnnObjectQualifier);
			string commandText = sql.ToString();
			using ( SqlConnection connection = new SqlConnection(connectionString) )
			{
				SqlCommand command = new SqlCommand(commandText, connection);
				connection.Open();
				using ( SqlDataReader reader = command.ExecuteReader() )
				{
					while ( reader.Read() )
					{
						userNames.Add(reader.GetString(reader.GetOrdinal("UserName")));
					}
					reader.Close();
				}
				connection.Close();
			}
			return userNames.ToArray();
		}

		#endregion

		#region Private methods

		private bool RoleExists(string roleName)
		{
#if DEBUG
			Log.Debug(Log.GetSource(), "roleName: {0}", roleName);
#endif
			bool exists = false;
			StringBuilder sql = new StringBuilder();
            sql.AppendFormat("SELECT COUNT(*) FROM ");
            sql.AppendFormat("( ");
            sql.AppendFormat("	SELECT ");
            sql.AppendFormat("	CAST(dbo.{0}Roles.PortalId AS VARCHAR(20)) + '.' + dbo.{0}Roles.RoleName AS RoleName ", this.DnnObjectQualifier);
            sql.AppendFormat("	FROM ");
            sql.AppendFormat("	dbo.{0}Roles ", this.DnnObjectQualifier);
            sql.AppendFormat(") Roles WHERE RoleName = @RoleName;");
			string commandText = sql.ToString();
			using ( SqlConnection connection = new SqlConnection(connectionString) )
			{
				SqlCommand command = new SqlCommand(commandText, connection);
				command.Parameters.AddWithValue("@RoleName", roleName);
				connection.Open();
				object returnValue = command.ExecuteScalar();
				connection.Close();
				if ( returnValue != null && returnValue != DBNull.Value )
				{
					exists = int.Parse(returnValue.ToString()) != 0;
				}
			}
			return exists;
		}

		private bool UsernameExists(string userName)
		{
#if DEBUG
			Log.Debug(Log.GetSource(), "userName: {0}", userName);
#endif
			bool exists = false;
            string commandText = string.Format("SELECT COUNT(*) FROM {0}Users WHERE Username = @Username;", this.DnnObjectQualifier);
			using ( SqlConnection connection = new SqlConnection(connectionString) )
			{
				SqlCommand command = new SqlCommand(commandText, connection);
				command.Parameters.AddWithValue("@Username", userName);
				connection.Open();
				object returnValue = command.ExecuteScalar();
				connection.Close();
				if ( returnValue != null && returnValue != DBNull.Value )
				{
					exists = int.Parse(returnValue.ToString()) != 0;
				}
			}
			return exists;
		}

		private bool IsValidCredentials(string userName, string password)
		{
#if DEBUG
			Log.Debug(Log.GetSource(), "userName: {0}, password: {1}", userName, password);
#endif
			bool isValid = false;
			try
			{
				isValid = Membership.ValidateUser(userName, password);
			}
			catch ( Exception ex )
			{
				Log.Fatal(Log.GetSource(), ex.ToString());	
			}
#if DEBUG
			Log.Debug(Log.GetSource(), "isValid: {0}", isValid);
#endif
			return isValid;
		}

		#endregion
	}
}